home *** CD-ROM | disk | FTP | other *** search
/ The Fatted Calf / The Fatted Calf.iso / Unix / Graphics / quarto / quarto.c next >
Encoding:
C/C++ Source or Header  |  1993-04-12  |  19.9 KB  |  733 lines

  1. /*
  2.  * quarto.c
  3.  * Quickie program to filter PostScript (EPS) files and...
  4.  *   - list the pages
  5.  *         ... | quarto -l ...
  6.  *   - extract selected pages
  7.  *         ... | quarto -p1,2,6-8 ...
  8.  *   - change page order (reverse, even, odd; etc)
  9.  *         ... | quarto -r ...           print in reverse order
  10.  *         ... | quarto -p odd ...       select the odd pages
  11.  *   - print "N-up" (quarto/octavo; N=2,3,4,6,8,9) layouts
  12.  *         ... | quarto -4 -c -b ...     print 4 pages per page
  13.  *
  14.  * Currently only tested and used with a NeXT printer + A4 (letter) paper,
  15.  * with input in portrait orientation.  It should be easy to fix for
  16.  * other media; check PBox, the test pattern, the calculation of 'Scale',
  17.  * and the macro 'pgx()' (used by the 'putNup()' routines).
  18.  *
  19.  * Enjoy at your own peril, but please send me any improvements.
  20.  *
  21.  * M. J. Hawley
  22.  * MIT media Laboratory
  23.  * 20 Ames Street
  24.  * Cambridge, MA 02139
  25.  * mike@media-lab.mit.edu
  26.  * Copyright (c) MIT Media Laboratory 1991.
  27.  * Burn before reading!  This means you!!
  28.  */
  29. #include <stdio.h>
  30. #include <sys/types.h>
  31.  
  32. /*
  33.  * Some general utilities ----------------------------------------
  34.  */
  35. char *av0;
  36. #define Case break; case
  37. #define Default break; default
  38. static char *_arg, *_argp; /* use by 'for_each_argument */
  39. extern char *av0;       /* will hold name of the command */
  40. #define argument  (_arg=(*_argp? _argp : av[++i==ac? --i: i]),_argp+=strlen(_argp),_arg)
  41. #define for_each_argument av0 = av[0]; for (i=1;i<ac && *av[i]=='-';i++)\
  42.                         for (_argp = &av[i][1]; *_argp;)\
  43.                                 switch(*_argp++)
  44.  
  45. extern char *malloc(), *index();
  46. extern int atoi();
  47. extern double atof();
  48.  
  49. stripnl(s) char *s; { /* remove trailing \n and space from 's' */
  50.     char *p = s + strlen(s)-1;
  51.     while (p>s && (*p=='\n' || *p == ' ')) *p-- = '\0';
  52. }
  53.  
  54. char *
  55. skipsp(s) char *s; {
  56.     while (*s==' ' || *s=='\t' || *s == '\n') ++s;
  57.     return s;
  58. }
  59.  
  60.  
  61. error(a,b,c,d,e,f) int a,b,c,d,e,f; { /* printf an error msg */
  62.     fprintf(stderr,(char *)a,b,c,d,e,f); fprintf(stderr,"\n");
  63. }
  64.  
  65. /* ---------------------------------------------------------------- */
  66.  
  67. FILE *Input, *Output;
  68. char *CurFile;
  69.  
  70. typedef struct { float x, y; } Point;
  71. typedef struct { Point o, c; } Rectangle;
  72. #define HS(r) (r.c.x-r.o.x)
  73. #define VS(r) (r.c.y-r.o.y)
  74.  
  75. Rectangle BBox = { 0, 0, 612, 792 }; /* bbox of the document */
  76.  
  77. Rectangle PBox = { 10, 14, 600, 780 }; /* Letter-sized page-bounding box */
  78.                  /* These values probably depend on the printer and
  79.                   * paper size; use the test pattern to adjust them for now.
  80.                   */
  81. Rectangle NullRect = {0,0,0,0};
  82.  
  83. int TestPattern = 0;    /* if true, overlay a test pattern on the page*/
  84. int ListPages = 0;      /* if true, list pages in the document (no print) */
  85.  
  86. #define _N_up 1
  87. int N_up = 1;           /* default # pages to fit on a sheet */
  88.  
  89. #define _Gutter 12.
  90. float Gutter= _Gutter;
  91. #define _CutMarks 0
  92. #define _Borders  1
  93. int CutMarks=_CutMarks, /* print cutmarks around shrunken pages */
  94.     Borders=_Borders;   /* print dashed borders around shrunken pages */
  95. int ReversePages = 0;
  96.  
  97. float Scale = .45;    /* Scale is normally set by the tiling routines; */
  98. int ScaleSet = 0;    /* made a user option just in case. */
  99. int Rotate = 0;         /* will be true when landscaping for -2,6,8 */
  100.  
  101. #define MaxP 1024
  102. #define Trailer (MaxP-1)
  103. int Page[MaxP], PageLen[MaxP];
  104. Rectangle PageBbox[MaxP], pageBbox;
  105. int NP=0;
  106.  
  107. findPageOffsets() {
  108.     char s[4192];
  109.     int i,prevpos=0,prevp=0,p;
  110.     FILE *f = Input;
  111.  
  112.     for(i=0;i<MaxP;i++){
  113.         Page[i]=PageLen[i]=0;
  114.         PageBbox[i] = NullRect;
  115.     }
  116.  
  117.     rewind(f);
  118.     while (fgets(s,sizeof s,f)){
  119.         if (strncmp(s,"%%Page: ",8)==0){
  120.             sscanf(s,"%%%%Page: %*s %d",&p);
  121.             Page[p] = ftell(f)-strlen(s);
  122.             PageLen[prevp] = Page[p] - prevpos;
  123.             prevp = p;
  124.             prevpos = Page[p];
  125.             NP++;
  126.         } else
  127.         if (strncmp(s,"%%Trailer",9)==0){
  128.             Page[Trailer] = ftell(f)-strlen(s);
  129.             PageLen[prevp] = Page[Trailer] - prevpos;
  130.             prevpos = Page[Trailer];
  131.         } else
  132.         if (strncmp(s,"%%BoundingBox:",14)==0){
  133.             char *q = skipsp(index(s,':')+1);
  134.             if (isdigit(*q))
  135.                 sscanf(q,"%f %f %f %f",
  136.                          &BBox.o.x,&BBox.o.y,&BBox.c.x,&BBox.c.y);
  137.         } else
  138.         if (strncmp(s,"%%PageBoundingBox:",18)==0){
  139.             Rectangle r;
  140.             char *q = skipsp(index(s,':')+1);
  141.             if (isdigit(*q)){
  142.                 sscanf(q,"%f %f %f %f", &r.o.x,&r.o.y,&r.c.x,&r.c.y);
  143.                 if (NP) PageBbox[NP] = r;
  144.             }
  145.         }
  146.     }
  147.     PageLen[Trailer] = ftell(f) - prevpos;
  148.     pageBbox = PageBbox[1]; /* hack ... */
  149.         /*
  150.          * Some NeXT programs put out PageBoundingBoxes
  151.          * with various origins for each page,
  152.          * but draw relative to the *first* PageBoundingBox.
  153.          * See "hack", below.
  154.          */
  155. }
  156.  
  157. showPageOffsets() {
  158.     char s[4192];
  159.     int i,j;
  160.     FILE *f = Input;
  161.  
  162.     printf("file: %s, %d pages\n",CurFile,NP);
  163.     printf("%8s %8s\n","offset","length");
  164.     for (i=j=0;j<=NP;i++){
  165.         if (Page[i] || i==0){
  166.             fseek(f,Page[i],0);
  167.             fgets(s,sizeof s,f);
  168.             printf("%8d %8d -- %s%s",Page[i], PageLen[i],i?"":"(header) ",s);
  169.             j++;
  170.         }
  171.     }
  172.     fseek(f,Page[Trailer],0);
  173.     fgets(s,sizeof s,f);
  174.     printf("%8d %8d -- (trailer) %s",Page[Trailer],PageLen[Trailer],s);
  175. }
  176.  
  177. static char *_s = (char *)0;
  178. static int _sn = 0;
  179.  
  180. char *
  181. readPage(n){
  182.     size_t r;
  183.     if (_s) *_s = '\0';
  184.     if (Page[n] || (n==0 && PageLen[n])){
  185.         if (_sn < PageLen[n]){
  186.             _sn = 2*PageLen[n];
  187.             _s = (_s? (char *)realloc(_s,_sn) : (char *)malloc(_sn));
  188.             if (!_s) error("%s: no memory!",av0), exit(1);
  189.         }
  190.         rewind(Input);
  191.         fseek(Input,Page[n],0);
  192.         *_s = '\0';
  193.         fread(_s,PageLen[n], (size_t)1, Input);
  194.     }
  195.     return _s;
  196. }
  197.  
  198. copyPage(n)
  199. /*
  200.  * Write the 'n'th page in 'f' onto 'o'.
  201.  */
  202. {
  203.     char *s = readPage(n);
  204.     if (*s) fwrite(s,PageLen[n],1,Output);
  205. }
  206.  
  207. P(a,b,c,d,e,f,g,h,i){
  208.     fprintf(Output,(char *)a,b,c,d,e,f,g,h,i);
  209.     fprintf(Output,"\n");
  210. }
  211.  
  212. putProlog(n){
  213.     char *date, *ctime();
  214.     time_t t;
  215.  
  216.     time(&t); date = ctime(&t); stripnl(date);
  217.     P("%%!PS-Adobe-2.0");
  218.     P("%%%%Creator: %s, file: %s",av0,CurFile);
  219.     P("%%%%CreationDate: %s",date);
  220.     P("%%%%Pages: (atend)",n);
  221.     P("%%%%BoundingBox: 0 0 612 792");  /* should have options to set */
  222.     P("%%%%EndComments");
  223.     P("");
  224.     P("%% ---------------------- (routines for \"%s\")",av0);
  225.     P("/q_W %f def  /q_H %f def  /q_margin 4 def",BBox.c.x,BBox.c.y);
  226.     P("/q_factor %f def",Scale);
  227.     P("/q_bbox {");
  228.     if (Borders){
  229.     P("    /q_y 0 q_factor mul def");
  230.     P("    /q_x 0 q_factor mul def");
  231.     P("    /q_w q_W q_margin 2 mul add def");
  232.     P("    /q_h q_H q_margin 2 mul add def");
  233.     P("    q_x q_margin sub q_y q_margin sub moveto");
  234.     P("    .2 setgray");
  235.     P("    0 setlinewidth");
  236.     P("    q_w 0 rlineto");
  237.     P("    0 q_h rlineto");
  238.     P("    0 q_w sub 0 rlineto");
  239.     P("    0 0 q_h sub rlineto");
  240.     P("    stroke");
  241.     }
  242.     P("} def");
  243.     P("");
  244.     P("/q_cuts {");
  245.     if (CutMarks){
  246.     P("    /q_y 0 q_factor mul q_margin sub def");
  247.     P("    /q_x 0 q_factor mul q_margin sub def");
  248.     P("    /q_w q_W q_margin 2 mul add def");
  249.     P("    /q_h q_H q_margin 2 mul add def");
  250.     P("    /q_d 10 q_factor mul def");
  251.     P("    0 setgray");
  252.     P("    0 setlinewidth");
  253.     P("    q_x q_d sub q_y moveto -1 -1 rmoveto");
  254.     P("        q_d 0 rlineto 0 0 q_d sub rlineto stroke");
  255.     P("    q_x q_d add q_w add q_y moveto 1 -1 rmoveto");
  256.     P("        0 q_d sub 0 rlineto 0 0 q_d sub rlineto stroke");
  257.     P("    q_x q_d add q_w add q_y q_h add moveto 1 1 rmoveto");
  258.     P("        0 q_d sub 0 rlineto 0 q_d rlineto stroke");
  259.     P("    q_x q_d sub q_y q_h add moveto -1 1 rmoveto");
  260.     P("        q_d 0 rlineto 0 0 q_d rlineto stroke");
  261.     }
  262.     P("} def");
  263.     P("");
  264.     P("/StartShrunkenPage {");
  265.     P("    /q_y exch def /q_x exch def");
  266.     P("    gsave");
  267.     P("    q_x q_y translate");
  268.     P("    q_factor q_factor scale");
  269.     if (Rotate) P("    -90 rotate");
  270.     P("} def");
  271.     P("");
  272.     P("/EndShrunkenPage {");
  273.     P("    grestore");
  274.     P("} def");
  275.     P("");
  276.     if (N_up!=1){
  277.        P("/oldshowpage {showpage} bind def");
  278.        P("/showpage { } bind def");
  279.     }
  280.     P("");
  281.     P("/q_circ { %% x y size ... draw circle + crosshairs");
  282.     P("    /q_size exch def /q_y exch def /q_x exch def");
  283.     P("    0 setlinewidth");
  284.     P("    q_x q_y moveto");
  285.     P("    q_size 0 rlineto");
  286.     P("    -2 q_size mul 0 rlineto");
  287.     P("    q_size 0 rlineto");
  288.     P("    0 q_size rlineto");
  289.     P("    0 -2 q_size mul rlineto");
  290.     P("    stroke");
  291.     P("    q_x q_y moveto");
  292.     P("    q_x q_y q_size 2 div 0 360 arc stroke");
  293.     P("} def");
  294.     P("");
  295.     P("/q_box { %% lx ly ux uy ... draw a box");
  296.     P("    /q_uy exch def /q_ux exch def /q_ly exch def /q_lx exch def");
  297.     P("    q_lx q_ly moveto");
  298.     P("    q_ux q_ly lineto");
  299.     P("    q_ux q_uy lineto");
  300.     P("    q_lx q_uy lineto");
  301.     P("    q_lx q_ly lineto");
  302.     P("    stroke");
  303.     P("} def");
  304.     P("");
  305.     P("/q_testPattern {");
  306.     P("    0 setgray 0 setlinewidth");
  307.     P("    %f %f %f %f q_box", PBox.o.x,PBox.o.y,PBox.c.x,PBox.c.y);
  308.     P("    %f %f moveto 8 0 rlineto stroke", PBox.o.x-4,VS(PBox)/2+PBox.o.y);
  309.     P("    %f %f moveto -8 0 rlineto stroke", PBox.c.x+4,VS(PBox)/2+PBox.o.y);
  310.     P("    %f %f moveto 0 8 rlineto stroke", HS(PBox)/2+PBox.o.x,PBox.o.y-4);
  311.     P("    %f %f moveto 0 -8 rlineto stroke", HS(PBox)/2+PBox.o.x,PBox.c.y+4);
  312.     P("    %f %f 5 q_circ",HS(PBox)/2+PBox.o.x,VS(PBox)/2+PBox.o.y);
  313.     P("} def");
  314.     P("");
  315.     P("%% ----------------------(end of \"%s\" routines)\n",av0);
  316.     copyPage(0);  /* put out the previous document header page */
  317. }
  318.  
  319. ShowPage(){
  320.     if (TestPattern) P("q_testPattern");
  321.     P("q_sheet restore\noldshowpage");
  322. }
  323.  
  324. putTrailer(n){
  325.     copyPage(Trailer);
  326.     P("%%%%Pages: %d %d",n,n);
  327. }
  328.  
  329. static Point
  330. center(r) Rectangle r; {
  331.     Point c;
  332.     c.x = HS(r)/2. + r.o.x;
  333.     c.y = VS(r)/2. + r.o.y;
  334.     return c;
  335. }
  336.  
  337. static Point
  338. add(a,b) Point a,b; {
  339.     a.x += b.x;
  340.     a.y += b.y;
  341.     return a;
  342. }
  343.  
  344. static Point
  345. sub(a,b) Point a,b; {
  346.     a.x -= b.x;
  347.     a.y -= b.y;
  348.     return a;
  349. }
  350.  
  351. pput(n,p) Point p; {
  352.     char *s = readPage(n);
  353.     Rectangle r;
  354.     if (*s){
  355.         s[2] = 'p';
  356.         r = pageBbox; /* PageBbox[n]; ... Thus endeth the hack. */
  357.     P("%f %f StartShrunkenPage",p.x,p.y);
  358.     P("%f %f translate",-1.*Scale*r.o.x,-1.*Scale*r.o.y);
  359.         fwrite(s,PageLen[n],1,Output);
  360.         P("EndShrunkenPage");
  361.     }
  362. }
  363.  
  364. float
  365. fmin(a,b) float a,b; {
  366.     return a<b? a : b;
  367. }
  368.  
  369. #define maxQ 16
  370.  
  371. #define up_decls \
  372.     int i, j, N = (NP+(N_up-1))/N_up, page[maxQ];\
  373.     float gap = Gutter;\
  374.     Point c, p, g, b, q[maxQ];\
  375.     g.x=g.y = gap/2.; b = BBox.c
  376.  
  377. #define nup_init() c = center(PBox); \
  378.                    b.x *= Scale; b.y *= Scale; \
  379.                    putProlog(N)
  380.  
  381. #define pgx(inc,d) pput(i*N_up+j+1,p); page[j] = *_s; q[j++]=p; p.d += inc
  382. #define putbox() for (j=0; j<N_up;j++) \
  383.                    if (page[j])\
  384.                     P("%f %f StartShrunkenPage q_bbox q_cuts EndShrunkenPage",\
  385.                         q[j].x,q[j].y); \
  386.                  ShowPage()
  387.  
  388.  
  389. put2up(){
  390.     up_decls;
  391.  
  392.     if (!ScaleSet)
  393.         Scale = fmin((VS(PBox)/2.-(2.*gap))/b.x,((HS(PBox)-(2.*gap))/b.y));
  394.     nup_init();
  395.  
  396.     for (i=0;i<N;i++){
  397.     P("\n%%%%Page: %d %d\n/q_sheet save def",i+1,i+1);
  398.         j = 0;
  399.  
  400.         p = c; p.y += g.x + b.x; p.x -= b.y/2.;
  401.         pgx(-(gap+b.x),y);
  402.         pgx(-(gap+b.x),y);
  403.     
  404.         putbox();   /* put out boxes, cut marks, & ShowPage */
  405.     }
  406.     return N;
  407. }
  408.  
  409. put3up(){
  410.     up_decls;
  411.  
  412.     if (!ScaleSet)
  413.         Scale = fmin((VS(PBox)/3.-(2.*gap))/b.x,((HS(PBox)-(2.*gap))/b.y));
  414.     nup_init();
  415.  
  416.     for (i=0;i<N;i++){
  417.     P("\n%%%%Page: %d %d\n/q_sheet save def",i+1,i+1);
  418.         j = 0;
  419.  
  420.         p = c; p.y += g.x + 1.5*b.x; p.x -= b.y/2.;
  421.         pgx(-(gap+b.x),y);
  422.         pgx(-(gap+b.x),y);
  423.         pgx(-(gap+b.x),y);
  424.     
  425.         putbox();   /* put out boxes, cut marks, & ShowPage */
  426.     }
  427.     return N;
  428. }
  429.  
  430. put6up(){
  431.     up_decls;
  432.  
  433.     if (!ScaleSet)
  434.         Scale = fmin((VS(PBox)/3.-(2.*gap))/b.x,((HS(PBox)/2.-(2.*gap))/b.y));
  435.     nup_init();
  436.  
  437.     for (i=0;i<N;i++){
  438.     P("\n%%%%Page: %d %d\n/q_sheet save def",i+1,i+1);
  439.         j = 0;
  440.  
  441.         p = c; p.y += gap + 1.5*b.x; p.x += g.x;
  442.         pgx(-(gap+b.x),y);
  443.         pgx(-(gap+b.x),y);
  444.         pgx(-(gap+b.x),y);
  445.     
  446.         p = c; p.y += gap + 1.5*b.x; p.x -= g.x + b.y;
  447.         pgx(-(gap+b.x),y);
  448.         pgx(-(gap+b.x),y);
  449.         pgx(-(gap+b.x),y);
  450.     
  451.         putbox();   /* put out boxes, cut marks, & ShowPage */
  452.     }
  453.     return N;
  454. }
  455.  
  456. put8up(){
  457.     up_decls;
  458.  
  459.     if (!ScaleSet)
  460.         Scale = fmin((VS(PBox)/4.-(2.*gap))/b.x,((HS(PBox)/2.-(2.*gap))/b.y));
  461.     nup_init();
  462.  
  463.     for (i=0;i<N;i++){
  464.     P("\n%%%%Page: %d %d\n/q_sheet save def",i+1,i+1);
  465.         j = 0;
  466.  
  467.         p = c; p.y += 1.5*gap + 2.*b.x; p.x += g.x;
  468.         pgx(-(gap+b.x),y);
  469.         pgx(-(gap+b.x),y);
  470.         pgx(-(gap+b.x),y);
  471.         pgx(-(gap+b.x),y);
  472.     
  473.         p = c; p.y += 1.5*gap + 2.*b.x; p.x -= g.x + b.y;
  474.         pgx(-(gap+b.x),y);
  475.         pgx(-(gap+b.x),y);
  476.         pgx(-(gap+b.x),y);
  477.         pgx(-(gap+b.x),y);
  478.     
  479.         putbox();   /* put out boxes, cut marks, & ShowPage */
  480.     }
  481.     return N;
  482. }
  483.  
  484. put4up(){
  485.     up_decls;
  486.  
  487.     if (!ScaleSet)
  488.         Scale = .5*fmin((HS(PBox)-(2.*gap))/b.x,(VS(PBox)-(2.*gap))/b.y);
  489.     nup_init();
  490.  
  491.     for (i=0;i<N;i++){
  492.     P("\n%%%%Page: %d %d\n/q_sheet save def",i+1,i+1);
  493.         j = 0;
  494.  
  495.         p = c; p.x -= g.x + b.x; p.y += g.y;
  496.         pgx(gap+b.x,x);
  497.         pgx(gap+b.x,x);
  498.         p = c; p.x -= g.x + b.x; p.y -= g.y + b.y;
  499.         pgx(gap+b.x,x);
  500.         pgx(gap+b.x,x);
  501.     
  502.         putbox();   /* put out boxes, cut marks, & ShowPage */
  503.     }
  504.     return N;
  505. }
  506.  
  507. put9up(){
  508.     up_decls;
  509.  
  510.     if (!ScaleSet)
  511.         Scale = .333*fmin((HS(PBox)-(3.*gap))/b.x,(VS(PBox)-(2.*gap))/b.y);
  512.     nup_init();
  513.  
  514.     for (i=0;i<N;i++){
  515.     P("\n%%%%Page: %d %d\n/q_sheet save def",i+1,i+1);
  516.         j = 0;
  517.  
  518.         p = c; p.x -= g.x + 1.5*b.x; p.y += g.y + .5*b.y;
  519.         pgx(g.x+b.x,x);
  520.         pgx(g.x+b.x,x);
  521.         pgx(g.x+b.x,x);
  522.  
  523.         p = c; p.x -= g.x + 1.5*b.x; p.y -= .5*b.y;
  524.         pgx(g.x+b.x,x);
  525.         pgx(g.x+b.x,x);
  526.         pgx(g.x+b.x,x);
  527.     
  528.         p = c; p.x -= g.x + 1.5*b.x; p.y -= g.y + 1.5*b.y;
  529.         pgx(g.x+b.x,x);
  530.         pgx(g.x+b.x,x);
  531.         pgx(g.x+b.x,x);
  532.  
  533.         putbox();   /* put out boxes, cut marks, & ShowPage */
  534.     }
  535.     return N;
  536. }
  537.  
  538. putpages(){
  539.     int i;
  540.     char *s, *p, *index();
  541.  
  542.     putProlog(NP);
  543.     for (i=1;i<=NP;i++){
  544.         s = readPage(i);
  545.         if (*s){
  546.             p = index(s,'\n');
  547.             if (p){
  548.                 p+1;
  549.                 fprintf(Output,"%%%%Page: %d %d\n",i,i);
  550.                 fwrite(p,PageLen[i] - (p-s),1,Output);
  551.             }
  552.         }
  553.     }
  554. }
  555.  
  556. char *SelectPages = (char *)0;
  557.  
  558. ReorderPages(){
  559.     int i,j;
  560.  
  561.     if (SelectPages){
  562.         char *s = skipsp(SelectPages);
  563.         int start=0,end;
  564.         int pages[MaxP], po[MaxP], pl[MaxP], np=0;
  565.         Rectangle pr[MaxP];
  566.         while (*s){
  567.             if (strncmp(s,"even",4)==0){
  568.                 for (i=2;i<=NP;i+=2) pages[np++]=i;
  569.                 s+=4;
  570.             } else
  571.             if (strncmp(s,"odd",3)==0){
  572.                 for (i=1;i<=NP;i+=2) pages[np++]=i;
  573.                 s+=3;
  574.             } else
  575.             if (*s == '-'){
  576.                 start = 1;
  577.                 ++s;
  578.                 end = atoi(s);
  579.                 while (isdigit(*s)) ++s;
  580.                 for (i=start;i<=end;i++) pages[np++] = i;
  581.             } else
  582.             if (isdigit(*s)){
  583.                 pages[np++] = atoi(s);
  584.                 while (isdigit(*s) || isspace(*s)) ++s;
  585.                 if (*s == '-'){
  586.                     ++s;
  587.                     while (*s && !isdigit(*s)) ++s;
  588.                     end = *s? atoi(s) : NP;
  589.                     while (*s && isdigit(*s)) ++s;
  590.                     for (i=pages[np-1]+1;i<=end;i++) pages[np++] = i;
  591.                 }
  592.             } else
  593.                 ++s;
  594.             while (*s== ',' || *s=='-' || isspace(*s)) ++s;
  595.         }
  596.         for (i=0;i<np;i++){
  597.             po[i] = Page[pages[i]];
  598.             pl[i] = PageLen[pages[i]];
  599.             pr[i] = PageBbox[pages[i]];
  600.         }
  601.         for (i=0;i<np;i++){
  602.             Page[i+1] = po[i];
  603.             PageLen[i+1] = pl[i];
  604.             PageBbox[i+1] = pr[i];
  605.         }
  606.         for (i=np+1;i<=NP;i++) Page[i] = PageLen[i] = 0;
  607.         NP = np;
  608.     }
  609.     if (ReversePages){
  610. #define swap(a,b) t = a; a = b; b = t
  611.         for (i=1,j=NP; i<j; i++,j--){
  612.             int t;
  613.             Rectangle r;
  614.             swap(Page[i],Page[j]);
  615.             swap(PageLen[i],PageLen[j]);
  616.             r = PageBbox[i]; PageBbox[i] = PageBbox[j]; PageBbox[j] = r;
  617.         }
  618.     }
  619. }
  620.  
  621. doFile(s) char *s; {
  622.     FILE *f = fopen(s,"r");
  623.     int n;
  624.     if (!f) return error("%s: couldn't open %s",av0,s);
  625.     Input = f;
  626.     findPageOffsets();
  627.  
  628.     if (ListPages)
  629.         showPageOffsets();
  630.     else {
  631.     ReorderPages();
  632.     
  633.     switch (N_up){
  634.     Case 2: n=put2up();
  635.     Case 3: n=put3up();
  636.     Case 4: n=put4up();
  637.     Case 6: n=put6up();
  638.     Case 8: n=put8up();
  639.     Case 9: n=put9up();
  640.     Default:n=putpages();
  641.     }
  642.     
  643.     putTrailer(n);
  644.     }
  645.     fclose(f);
  646. }
  647.  
  648. savetmp(s,f) char *s; FILE *f;
  649. /* 
  650.  * Copy 'f' into a tmp file; put the name in s,
  651.  * and return 1 if success.
  652.  */
  653. {
  654.     FILE *o;
  655.     int n, F, O;
  656.     char b[8192];
  657.     strcpy(s,"/tmp/quartoXXXXXX");
  658.     mktemp(s);
  659.     strcat(s,".eps");
  660.     if (o = fopen(s,"w")){
  661.         F = fileno(f);
  662.         O = fileno(o);
  663.         while ((n=read(F,b,sizeof b))>0)
  664.             write(O,b,n);
  665.         fclose(o);
  666.         return 1;
  667.     } else
  668.         return 0;    
  669. }
  670.  
  671. use(){
  672.     error("use: %s [-[234689]] [-p <pages>] [-bcdlr] [-g #] [PS file]",av0);
  673.     error("Select pages from a PostScript file, and print in a tiled format.");
  674.     error("(The input must contain EPS-like directives '%%%%Page:...')");
  675.     error("  -p... print the given pages; e.g., '-p1,2,4-8,12-'");
  676.     error("        '-p -4' prints through page four;");
  677.     error("        '-p 12-' prints from page 12 through the end;");
  678.     error("        numbers are original ordinal page numbers, starting at 1");
  679.     error("        also, '-p even', '-p odd', '-p1-4,1-4,1-4,1-4' etc.");
  680.     error("  -2    print 2 tiled, shrunken pages per page;");
  681.     error("        # may be 2,3,4,6,8 or 9 (default 1);");
  682.     error("        portrait or landscape layout will be chosen accordingly.");
  683.     error("  -b    %sprint borders around each tiled page.",
  684.                    _Borders?"don't ":"");
  685.     error("  -c    %sprint cut-marks for each tiled page.",
  686.                    _CutMarks?"don't ":"");
  687.     error("  -d    overlay a test pattern on the page.");
  688.     error("        (a box around the paper sheet border, ");
  689.     error("         and a circle and cross in the middle;");
  690.     error("         intended to help calibrate for different printers)");
  691.     error("  -g #  set the gap between tiles to # (%3f)",_Gutter);
  692.     error("  -l    list the pages+offsets (don't output any PostScript).");
  693.     error("  -r    reverse the page order");
  694.     exit(1);
  695. }
  696.  
  697. main(ac,av) char *av[]; {
  698.     int i;
  699.  
  700.     for_each_argument {
  701.     Case 'l': ListPages = 1;
  702.     Case 'p': SelectPages = argument;
  703.     Case 'r': ReversePages = 1;
  704.     Case 'd': TestPattern = !TestPattern;
  705.     Case 'c': CutMarks = !CutMarks;
  706.     Case 'b': Borders = !Borders;
  707.     Case '1': N_up = 1;
  708.     Case '2': N_up = 2; Rotate = 1;
  709.     Case '3': N_up = 3; Rotate = 1;
  710.     Case '4': N_up = 4;
  711.     Case '6': N_up = 6; Rotate = 1;
  712.     Case '8': N_up = 8; Rotate = 1;
  713.     Case '9': N_up = 9;
  714.     Case 'g': Gutter = atof(argument);
  715.     Case 's': Scale = atof(argument); ScaleSet++; /* not mentioned as option */
  716.     Default : use();
  717.     }
  718.  
  719.     Output = stdout;
  720.     if (i==ac){
  721.         char s[1024];
  722.         if (savetmp(s,stdin))
  723.             CurFile="stdin", doFile(s), unlink(s);
  724.         else
  725.             error("%s: couldn't save stdin to a tmp file.",av0), exit(1);
  726.     } else
  727.     if ((i+1)==ac)
  728.         CurFile=av[i], doFile(av[i++]);
  729.     else
  730.         error("%s: accepts only one file, or stdin...\n",av0), use();
  731.     exit(0);
  732. }
  733.